--- title: patrones_espacio_temporales keywords: fastai sidebar: home_sidebar summary: "Métodos para explorar los patrones espacio-temporales de la delincuencia y su evolución" description: "Métodos para explorar los patrones espacio-temporales de la delincuencia y su evolución" nb_path: "01_hotspots.ipynb" ---
carpetas = get_carpetas_from_api(10000)
carpetas = carpetas.to_crs(32614)
malla = construye_malla(carpetas, 200)
def ajusta_bandwidth_kde(datos, bandwidth_space, size=1000,
malla=None, n_jobs=-1, metric="euclidean"):
""" Regresa el valor de bandwidth con mejor log likelihood.
Parametros:
datos (GeoDataFrame): víctimas o carpetas
bandwith_space (np.linspace): con el espacio de búsqueda
size (float): Tamaño de la celda (en las unidades de la proyección).
Si se especifica malla se ignora
malla (np.meshgrid): la malla en la que se va a ajustar el KDE, si es None se calcula
n_jobs (int): número de procesos a usar (default = -1)
metric (str): métrica a usar para calcular las distancias (default euclidean)
"""
if malla is None:
xx, yy = construye_malla(datos, size)
else:
xx = malla[0]
yy = malla[1]
xy_sample = np.vstack([yy.ravel(), xx.ravel()]).T
x = datos.geometry.x.to_numpy()
y = datos.geometry.y.to_numpy()
xy_train = np.vstack([y, x]).T
grid = GridSearchCV(KernelDensity(metric=metric), bandwidth_space, n_jobs=n_jobs)
grid.fit(xy_train)
return grid.best_estimator_.bandwidth
Esta función ajusta el ancho de banda para calcular un KDE en 2 dimensiones. Puede ser muy tardado.
El ancho de banda se puede calcular en cualquyier sistema de coordenadas, sin embargo es conveniente proyectarlos a coordenadas planas.
Primero ajustamos especificando el tamaño de la malla
params = {'bandwidth': np.linspace(10, 10000, 100)}
bw = ajusta_bandwidth_kde(carpetas, params, size=1000)
print(bw)
También podemos especificar la malla primero
params = {'bandwidth': np.linspace(10, 10000, 100)}
bw = ajusta_bandwidth_kde(carpetas, params, malla=malla)
print(bw)
Si no mandamos una malla como argumento, se calcula utilizando size
xx, yy, zz = kde2D(carpetas, bw, size=1000)
fig = plt.figure(figsize=(10,10))
ax = plt.axes(projection='3d')
ax = ax.plot_surface(xx, yy, zz,cmap='viridis', edgecolor='none')
También se puede especificar una malla con el tamaño deseado y calcular el KDE
malla = construye_malla(carpetas, 1000)
xx, yy, zz = kde2D(carpetas, bw, malla=malla)
fig = plt.figure(figsize=(10,10))
ax = plt.axes(projection='3d')
ax = ax.plot_surface(xx, yy, zz,cmap='viridis', edgecolor='none')
Esta función se usa dentro de serie_tiempo_kde_categoria y no se llama directamente.
carpetas = get_historico_carpetas()
carpetas = agregar_categorias_carpetas(carpetas)
carpetas = carpetas.to_crs(32614)
fechas = pd.date_range(start='1/1/2020', end='3/1/2020', freq='M').to_list()
categorias = ["Homicidios dolosos"]
datos = get_lista_datos(carpetas, fechas, categorias, "30 days")
datos[0].head()
Regresa la serie de tiempo de KDEs para las categorías seleccionadas. La serie de tiempo se construye a partir de los datos de incidentes (víctimas o carpetas de investigación) y se agrega en intervalos de tiempo definidos por el parámetro fechas. En este parámetro deben venir los extremos derechos de los intervalos (regulares) para agregar y debe coincidir con el parámetro offset que se usa internamente para calcular el primer intervalo de agregación.
Cuando esta función se usa para calcular diréctamente los Hotspots pára una categoría, no es necesario llamarla con una malla, la función la va a calcular a partir de los datos, lo que es necesario es fijar el parámetro size que indica el tamaño de los pixeles en la malla resultante.
Para usar la función es necesario que labase de incidentes contenga la columna categoria.
fechas = pd.date_range(start='1/1/2019', end='1/1/2020', freq='M').to_list()
categorias = ["Homicidios dolosos"]
xr_kde = KDE_hotspots(carpetas, fechas, 1000, categorias, "30 days", bw=1000)
assert type(xr_kde) == xr.DataArray
xr_kde
%%output size=200 widget_location='bottom'
xr_dataset = gv.Dataset(series,
kdims=["tiempo", "longitud", "latitud"],
crs=crs.UTM(zone=14))
(xr_dataset
.to(gv.Image, ["longitud", "latitud"])
.opts(alpha=0.7) * gv.tile_sources.CartoDark())
En el ejemplo anterior seleccionamos por el índice (xr_kde.isel(tiempo=0)), también podemos seleccionar por el valor de la etiqueta
Calculamos la serie de mapas de razones
fechas = pd.date_range(start='1/1/2019', end='1/1/2020', freq='M').to_list()
categorias = ["Homicidios dolosos"]
series = razones_de_incidentes(carpetas,fechas, ["Homicidios dolosos"], "30 days", 1000, 1000)
series
El resultado es un xr.Dataset que contiene las dos series (base y de categoría). Con geoviews podemos, por ejemplo, comparar el resultado de KDE_hotspots para la categoría especificada con la serie para todas las demás categorías.
%%output size=200 widget_location='bottom'
xr_dataset = gv.Dataset(series,
kdims=["tiempo", "longitud", "latitud"],
vdims=["Serie categorias"],
crs=crs.UTM(zone=14))
(xr_dataset
.to(gv.Image, ["longitud", "latitud"], ["Serie categorias"])
.opts(alpha=0.7) * gv.tile_sources.CartoDark())
Podemos visualizar las tres superficies resultantes
Esta función calcula los Hotspots basados en la idea de SMER (Social Media Events Ratio). El resultado es un xr.Dataset con:
La idea es que esta función nos permite ver qué tanto resalta la categoría seleccionada con respecto al resto de las categorías y cómo se va moviendo en el tiempo. Es una especie de medida del saliency de la categoría de interés y nos permite identificar lugares en donde esta categŕía empieza a desplazar a las demás.
La función regresa un xr.Dataset con variables para cada una de las series.
fechas = pd.date_range(start='1/1/2019', end='1/1/2020', freq='M').to_list()
categorias = ["Homicidios dolosos"]
series = smer_KDE(carpetas, fechas, ["Homicidios dolosos"], "30 days", 1000, 1000)
series
En general, las variables importantes de esta función son la Intensidad y la significancia de los hotspots identificados con SMER. Podemos ver mapas de ambos de forma fácil usando geoviews
%%output widget_location='bottom', size=150
xr_dataset = gv.Dataset(series,
kdims=["tiempo", "longitud", "latitud"],
vdims=["intensidad", "p_values"],
crs=crs.UTM(zone=14))
int_gv = (xr_dataset
.to(gv.Image, ["longitud", "latitud"], ["intensidad"])
.opts(alpha=0.7) * gv.tile_sources.CartoDark())
p_gv = (xr_dataset
.to(gv.Image, ["longitud", "latitud"], ["p_values"])
.opts(alpha=0.7) * gv.tile_sources.CartoDark())
lo = int_gv + p_gv
lo